// npx babel-node object_new_methods.js

// Object.is

let test01 = function() {
    let _
    _ = Object.is('foo', 'foo')
    console.log(_)

    _ = Object.is({}, {})
    console.log(_)
}

// test01()

let test02 = function() {
    let _

    _ = +0 === -0
    console.log(_)

    _ = NaN === NaN
    console.log(_)

    _ = Object.is(+0, -0)
    console.log(_)

    _ = Object.is(NaN, NaN)
    console.log(_)
}

// test02()


// Object.assign

let test03 = function() {
    const target = { a: 1 }

    const source1 = { b: 2 }
    const source2 = { c: 3 }

    Object.assign(target, source1, source2)
    console.log(target)
}

// test03()

// 注意，如果目标对象与源对象有同名属性，或多个源对象有同名属性，则后面的属性会覆盖前面的属性。

let test04 = function() {
    const target = { a: 1, b: 1 }

    const source1 = { b: 2, c: 2 }
    const source2 = { c: 3 }

    Object.assign(target, source1, source2)
    console.log(target)
}

// test04()

// 如果只有一个参数，Object.assign会直接返回该参数。

let test05 = function() {
    const obj = {a: 1}
    let _ = Object.assign(obj) === obj
    console.log(_)
}

// test05()

// 如果该参数不是对象，则会先转成对象，然后返回。

let test06 = function() {
    let _

    _ = Object.assign(2)
    console.log(_)

    _ = typeof _

    console.log(_)
}

// test06()

// 由于undefined和null无法转成对象，所以如果它们作为参数，就会报错。

let test07 = function() {
    
    // Object.assign(undefined) // 报错
    // Object.assign(null) // 报错
}

// test07()

let test08 = function(){
    const v1 = 'abc'
    const v2 = true
    const v3 = 10

    const obj = Object.assign({}, v1, v2, v3)
    console.log(obj)
}

// test08()

let test09 = function() {
    let _

    _ = Object(true)
    console.log(_)
    _ = Object.getOwnPropertyDescriptors(_)
    console.log(_)
    

    _ = Object(10)
    console.log(_)
    _ = Object.getOwnPropertyDescriptors(_)
    console.log(_)

    _ = Object('abc')
    console.log(_)
    _ = Object.getOwnPropertyDescriptors(_)
    console.log(_)
}

// test09()

// 属性名为 Symbol 值的属性，也会被Object.assign拷贝。

let test10 = function() {
    let _

    _ = Object.assign({ a: 'b' }, { [Symbol('c')]: 'd' })

    console.log(_)
}

// test10()

// 取值函数的处理

let test11 = function() {
    const source = {
        get foo() {
            return 2
        }
    }

    const target = {}

    Object.assign(target, source)

    console.log(target)
}

// test11()

// Object.assign方法有很多用处。

// （1）为对象添加属性

let test12 = function() {
    class Point {
        constructor(x, y) {
            Object.assign(this, {x, y})
        }
    }

    let p = new Point(1, 2)
    console.log(p)

    // let p2 = Object.create(Point)
}

// test12()

// （2）为对象添加方法

let test13 = function() {
    class SomeClass {}
    Object.assign(SomeClass.prototype, {
        someMethod(arg1, arg2) {
            // ....
            console.log('invoke someMethod')
        },
        anotherMethod() {
            // ...
            console.log('invoke anotherMethod')
        }
    })

    let s = new SomeClass()
    s.someMethod()
    s.anotherMethod()
}

// test13()

let test14 = function() {
    const obj = {
        foo: 123,
        get bar() { return 'abc' }
    }

    let _
    _ = Object.getOwnPropertyDescriptors(obj)
    console.log(_)
}

// test14()

// Object.assign 无法拷贝 get 属性和 set 属性

let test15 = function() {
    const source = {
        set foo(value) {
            console.log(value)
        }
    }

    const target = {}
    Object.assign(target, source)

    let _
    _ = Object.getOwnPropertyDescriptor(target, 'foo')
    console.log(_)
}

// test15()

// Object.getOwnPropertyDescriptors()方法配合Object.defineProperties()方法，就可以实现正确拷贝。

let test16 = function() {
    const source = {
        set foo(value) {
            console.log(value)
        }
    }

    const target = {}
    Object.defineProperties(target, Object.getOwnPropertyDescriptors(source))
    let _
    _ = Object.getOwnPropertyDescriptor(target, 'foo')
    console.log(_)
}

// test16()

// 上面代码中，两个对象合并的逻辑可以写成一个函数。

let test17 = function() {
    const shallowMerge = (target, source) => Object.defineProperties(
        target,
        Object.getOwnPropertyDescriptors(source)
    )
}


// 实现浅拷贝

let test18 = function() {
    const clone = Object.create(Object.getPrototypeOf(obj),
        Object.getOwnPropertyDecriptors(obj))

    const shallowClone = (obj) => Object.create(
        Object.getPrototypeOf(obj),
        Object.getOwnPropertyDescriptors(obj)
    )
}

// Object.getOwnPropertyDescriptors()也可以用来实现 Mixin（混入）模式。

let test19 = function() {
    let mix = (object) => ({
        with: (...mixins) => mixins.reduce(
            (c, mixin) => Object.create(
                c, Object.getOwnPropertyDescriptors(mixin)
            ),
            object
        )
    })

    let a = {a: 'a'}
    let b = {b: 'b'}
    let c = {c: 'c'}
    let d = mix(c).with(a, b)

    console.log(d)
    console.log(d.c)
    console.log(d.b)
    console.log(d.a)
}

// test19()

// Object.setPrototypeOf

let test20 = function() {
    let proto = {}
    let obj = {x: 10}
    Object.setPrototypeOf(obj, proto)

    proto.y = 20
    proto.z = 40

    console.log(obj)
    console.log(obj.x)
    console.log(obj.y)
    console.log(obj.z)
}

// test20()

// Object.setPrototypeOf

function Rectangle() {
    // ...
}

let test21 = function() {
    const rec = new Rectangle()

    let _

    _ = Object.getPrototypeOf(rec) === Rectangle.prototype
    console.log(_)

    Object.setPrototypeOf(rec, Object.prototype)
    _ = Object.getPrototypeOf(rec) === Rectangle.prototype
    console.log(_)
}

// test21()

let test22 = function() {
    let _

    _ = Object.getPrototypeOf(1)
    console.log(_)
}

// test22()

let test23 = function() {
    let obj = { foo: 'bar', baz: 43 }
    let _ = Object.keys(obj)

    console.log(_)
}

// test23()

let test24 = function() {
    let {keys, values, entries} = Object

    let obj = { a:1, b:2, c:3 }

    for(let key of keys(obj)) {
        console.log(key)
    }

    for(let val of values(obj)) {
        console.log(val)
    }

    for(let [key, val] of entries(obj)) {
        console.log([key, val])
    }
}

// test24()

// Object.values只返回对象自身的可遍历属性。

let test25 = function() {
    const obj = Object.create({}, {p: {value: 42}})
    let _
    _ = Object.values(obj)
    console.log(_)
}

// test25()

// 只要把enumerable改成true，Object.values就会返回属性p的值。

let test26 = function() {
    const obj = Object.create({}, {p: 
        {
            value: 42,
            enumerable: true
        }
    })
    let _
    _ = Object.values(obj)
    console.log(_)
}

// test26()

// Object.fromEntries()

let test27 = function() {
    let _
    const entries = new Map([
        ['foo', 'bar'],
        ['baz', 42]
    ])
    _ = Object.fromEntries(entries)
    console.log(_)

    const map = new Map().set('foo', true).set('bar', false)
    _ = Object.fromEntries(map)
    console.log(_)
}

test27()
